//
// Copyright (c) Microsoft Corporation.  All rights reserved.
//
//
// Use of this sample source code is subject to the terms of the Microsoft
// license agreement under which you licensed this sample source code. If
// you did not accept the terms of the license agreement, you are not
// authorized to use this sample source code. For the terms of the license,
// please see the license agreement between you and Microsoft or, if applicable,
// see the LICENSE.RTF on your install media or the root of your tools installation.
// THE SAMPLE SOURCE CODE IS PROVIDED "AS IS", WITH NO WARRANTIES OR INDEMNITIES.
//
//
// (C) Copyright 2006 Marvell International Ltd.
// All Rights Reserved
//
//------------------------------------------------------------------------------
//
//  File:  main.c
//
//  Core routines for the Intel PXA3XX bootloader.
//
#include <windows.h>
#include <nkintr.h>
#include <monahans.h>
#include <Littleton.h>
#include <oal_memory.h>
#include <pcireg.h>
#include <fmd.h>
#include <bsp.h>
#include "loader.h"
#include <bootpart.h>
#include "xllp_pm.h"
#include "xllp_pm_dvfm.h"
#include "monahans_base_regs.h"
#include "xllp_defs.h"
#include "xllp_udc.h"
#include "xllp_lcd_plat.h"
#include "xllp_pm_micco.h"
#include "xllp_i2c.h"
#include "xllp_mfp_proc.h"

#include <flash_cfg.h>




//Included for the SD/MMC boot option
#include "SDMMC.h"
#include "FAT.h"

//Include those functions related to dbg serail output/input
#include "..\..\common\dbgserial\dbgserial.c"


#define IsValidVirtualAddr(x) (((x>0x80000000)&&(x<0xBFFFFFFF))? 1: 0)
//------------------------------------------------------------------------------
// Local variables.
//
static PCI_REG_INFO g_FlashAddress;
BOOL      g_DownloadImage = TRUE;
BOOL      g_InitEther = FALSE;
BOOL      g_IPLDownload = FALSE;
BOOL      g_SDMMCDownload = FALSE;
//------------------------------------------------------------------------------
// Global variables.
//
DWORD     EdbgDebugZone;
FlashInfo g_FlashInfo;
//BSP_ARGS *g_pBSPArgs = (BSP_ARGS *) IMAGE_SHARE_ARGS_UA_START_EBOOT;
BSP_ARGS *g_pBSPArgs = (BSP_ARGS *)IMAGE_SHARE_ARGS_UA_START;

EBOOT_CFG    *g_pEbootCFG = &(((BSP_ARGS *)IMAGE_SHARE_ARGS_UA_START)->eBootCFG);

static BSP_ARGS g_OldBSPArgs;

EDBG_ADDR g_DeviceAddr; // NOTE: global used so it remains in scope throughout download process
                        // since eboot library code keeps a global pointer to the variable provided.
IMAGE_TYPE g_ImageType = IMAGE_TYPE_UNKNOWN;
EBOOT_CONTEXT g_EBootContext = {0, 0, NULL, 0, 0, 0};
BOOL g_WriteImageToNAND = FALSE;
BOOL g_SetEthernetKITL = FALSE;
BOOL g_JumpDirectlyIntoRAM = FALSE;

BOOL g_bDebugSerialEnable = TRUE;

//------------------------------------------------------------------------------
// Local function prototypes.
//
BOOL LoadEBSPCFG(BSP_ARGS *BSPCFG);
BOOL StoreBSPCFG(BSP_ARGS *BSPCFG);

void ResetDefaultBSPCFG(BSP_ARGS *pEbootCFG);

static void SetBootMe(EBOOT_CFG *pEbootCFG);
void SetMACAddress(BSP_ARGS *pEbootCFG);
extern DWORD DetectLCDPanel();

extern UINT16 hex_atoi(const char * hexstr);

BOOL OEMVerifyMemory(DWORD dwStartAddr, DWORD dwLength);
void RunTests(void);
void OEMWriteDebugLED(UINT16 Index, DWORD Pattern);
void LoadImageDDR(BOOL enableJump);
void JumpToImageDDR(void);
extern void InitTimer(void);
static BOOL InitEthernet(void);

void ConfigPrint();
void pic_init(BOOL is_special_eco);

#define XLLP_ACCR_S_XL(accr, xl)		(((accr) & ~0x1F) | xl)
#define XLLP_ACCR_S_XN(accr, xn)		((((accr) & ~(0x7 << 8)) | ((xn) << 8)))
#define XLLP_ACCR_S_SMCFS(accr, smcfs)	((((accr) & ~(0x7 << 23)) | ((smcfs) << 23)))
#define XLLP_ACCR_S_SFLFS(accr, sflfs)	((((accr) & ~(0x3 << 18)) | ((sflfs) << 18)))
#define XLLP_ACCR_S_HSS(accr, hss)		((((accr) & ~(0x3 << 14)) | ((hss) << 14)))
#define XLLP_ACCR_S_DMCFS(accr, dmcfs)	((((accr) & ~(0x3 << 12)) | ((dmcfs) << 12)))
#define XLLP_ACCR_S_XSPCLK(accr, xspclk)	((((accr) & ~(0x3 << 16)) | ((xspclk) << 16)))
#define XLLP_ACCR_S_D0CS(accr, d0cs)	((((accr) & ~(0x1 << 26)) | ((d0cs) << 26)))
#define XLLP_ACCR_S_VAUF(accr, vauf)	((((accr) & ~(0x3 << 28)) | ((vauf) << 28)))


#define XLLP_PM_ACCR_SMCFS_78M	0x0
#define XLLP_PM_ACCR_SMCFS_104M	0x2
#define XLLP_PM_ACCR_SMCFS_208M	0x5

/* Set SFLFS clock in ACCR */
#define XLLP_PM_ACCR_SFLFS_104M	0x0
#define XLLP_PM_ACCR_SFLFS_156M	0x1
#define XLLP_PM_ACCR_SFLFS_208M	0x2
#define XLLP_PM_ACCR_SFLFS_312M	0x3

/* Set HSS clock in ACCR */
#define XLLP_PM_ACCR_HSS_104M	0x0
#define XLLP_PM_ACCR_HSS_156M	0x1
#define XLLP_PM_ACCR_HSS_208M	0x2

/* Set DMCFS in ACCR */
#define XLLP_PM_ACCR_DMCFS_26M	0x0
#define XLLP_PM_ACCR_DMCFS_260M	0x3

/* Set VAUF, MVED clock, in ACCR */
#define XLLP_PM_ACCR_VAUF_104M  0x0
#define XLLP_PM_ACCR_VAUF_156M  0x1
#define XLLP_PM_ACCR_VAUF_208M  0x2
#define XLLP_PM_ACCR_VAUF_78M   0x3

/* Set df_clkdiv in MEMCLKCFG */
#define XLLP_PM_DF_CLKDIV_1 0x1
#define XLLP_PM_DF_CLKDIV_2 0x2
#define XLLP_PM_DF_CLKDIV_4 0x3

/* set empi_clkdiv in MEMCLKCFG  */
#define XLLP_PM_EMPI_CLKDIV_1 0x1
#define XLLP_PM_EMPI_CLKDIV_2 0x2
#define XLLP_PM_EMPI_CLKDIV_4 0x3

#include "BL_Battery.h"

void DumpACCR(void)
{
    volatile UINT32 *pACCR = OALPAtoVA(0x41340000, FALSE);

    EdbgOutputDebugString("ACCR = 0x%x\r\n", *pACCR);
}

void BLMenu();

/// Judge the reset reason and whether can skip copying image 
/// @param  bsp_args  [IN]BSP arguments
/// @return     TRUE ( can skip image copy); FALSE (need to copy image).
///
/// Reads the ARSR to determine if we can just jump to the next image in the 
/// chain. Updates ARSR variable as well.
/// Actually mobm has implemented this functionality also.
BOOL SkipImageCopy(BSP_ARGS * bsp_args)
{
    volatile XLLP_PM_BPMU_T *v_pPMURegs;
    BOOL     retval;
    DWORD    arsr;

    v_pPMURegs = (volatile XLLP_PM_BPMU_T *) OALPAtoVA(MONAHANS_BASE_REG_PA_BPMU, FALSE);

    arsr = v_pPMURegs->arsr;
    bsp_args->ARSR = arsr;
    // Clear ARSR
//    v_pPMURegs->arsr = 0xF;

    EdbgOutputDebugString("Reset Cause: ");

    if (arsr & XLLP_ARSR_HWR)
    {
        EdbgOutputDebugString("Hardware reset\r\n");
        retval = FALSE;
    }
    else if (arsr & XLLP_ARSR_WDT)
    {
        EdbgOutputDebugString("Watchdog reset\r\n");
        retval = FALSE;
    }
    else if (arsr & XLLP_ARSR_LPMR)
    {
        EdbgOutputDebugString("Low power modem reset\r\n");
		retval = FALSE;
    }
    else if (arsr & XLLP_ARSR_GPR)
    {
        EdbgOutputDebugString("GPIO reset\r\n");
        retval = FALSE;
    }
    else
    {
        EdbgOutputDebugString("<Unknown>\r\n");
        retval = FALSE;
    }

    return retval;
}

//------------------------------------------------------------------------------
//
//  Function:  main
//
//  Bootloader main routine.
//
void main(void)
{

    // Common boot loader (blcommon) main routine.
    //
    BootloaderMain();

    // Should never get here.
    //
    SpinForever();

}

//------------------------------------------------------------------------------
//
//  Function:  OEMDebugInit
//
//  Initialize debug serial UART.
//
BOOL OEMDebugInit(void)
{

    InitDebugSerial((UINT32)MONAHANS_BASE_REG_PA_SIRUART,  FALSE);
    // Initialize the flash interface (needed so we can determine which serial
    // port to use for bootloader and OS debug message output).
    //
    if (!FlashInit((UINT32) NULL, (UINT32) NULL))
    {
        // Load default bootloader configuration settings.
        //
        EdbgOutputDebugString("ERROR: flash initialization failed - loading bootloader defaults...\r\n");
        ResetDefaultBSPCFG(g_pBSPArgs);

    }
    else
    {
        // Get flash info
        if (FMD_GetInfo(&g_FlashInfo) == FALSE)
        {
            EdbgOutputDebugString("ERROR: OEMDebugInit: FMD_GetInfo call failed!\r\n");
            ResetDefaultBSPCFG(g_pBSPArgs);
        }
        else
        {
            // Load the bootloader configuration from flash (menu settings).
            //
            LoadEBSPCFG(g_pBSPArgs);
        }
    }



    // Clear the hex LEDs.
    //
    OEMWriteDebugLED(0, 0);

    // Set up optional bootloader function pointers.
    //
    g_pOEMVerifyMemory = OEMVerifyMemory;

    InitTimer();

    return(TRUE);
}

void UpdateImageAddr(BLOCK_ID imageStartBlock, UINT16 numBlocks)
{
    EdbgOutputDebugString("UpdateImageAddr start =%d, numBlocks = %d\r\n", imageStartBlock, numBlocks);
    g_pEbootCFG->dwPhyStartBlock = imageStartBlock;
    g_pEbootCFG->dwPhyNumBlocks = numBlocks;
    StoreBSPCFG(g_pBSPArgs);

}



/// Use XLLP routines to switch between various operating points 
/// @param  i, specify which OP is going to be used.
void DoChangeFreq(int i)
{


    XLLP_PM_DVFM_PARAM param;
    XLLP_CLKMGR_T * clk=(XLLP_CLKMGR_T*)OALPAtoVA(MONAHANS_BASE_REG_PA_CLKMGR, FALSE);
    param.mts = 1;
    switch(i)
    {
    case BSP_ARGS_RUN_FREQ_2:  //104
        param.xl = 8;
        param.xn = 0x1;
        param.t  = 0;

        clk->accr=XLLP_ACCR_S_HSS(clk->accr, XLLP_PM_ACCR_HSS_104M);
        clk->accr=XLLP_ACCR_S_SFLFS(clk->accr, XLLP_PM_ACCR_SFLFS_104M);
        clk->accr=XLLP_ACCR_S_SMCFS(clk->accr, XLLP_PM_ACCR_SMCFS_78M);
        clk->accr=XLLP_ACCR_S_VAUF(clk->accr, XLLP_PM_ACCR_VAUF_78M);

        break;
    case BSP_ARGS_RUN_FREQ_3:  //208
        param.xl = 16;
        param.xn = 1;
        param.t  = 0;

        clk->accr=XLLP_ACCR_S_HSS(clk->accr, XLLP_PM_ACCR_HSS_104M);
        clk->accr=XLLP_ACCR_S_SFLFS(clk->accr, XLLP_PM_ACCR_SFLFS_156M);
        clk->accr=XLLP_ACCR_S_SMCFS(clk->accr, XLLP_PM_ACCR_SMCFS_104M);
        clk->accr=XLLP_ACCR_S_VAUF(clk->accr, XLLP_PM_ACCR_VAUF_78M);

        break;
    case BSP_ARGS_RUN_FREQ_4:  //416
        param.xl = 16;
        param.xn = 2;
        param.t  = 1;

        clk->accr=XLLP_ACCR_S_HSS(clk->accr, XLLP_PM_ACCR_HSS_156M);
        clk->accr=XLLP_ACCR_S_SFLFS(clk->accr, XLLP_PM_ACCR_SFLFS_208M);
        clk->accr=XLLP_ACCR_S_SMCFS(clk->accr, XLLP_PM_ACCR_SMCFS_104M);
        clk->accr=XLLP_ACCR_S_VAUF(clk->accr, XLLP_PM_ACCR_VAUF_104M);

        break;

    case BSP_ARGS_RUN_FREQ_5:  //624
        param.xl = 24;
        param.xn = 2;
        param.t  = 1;
        clk->accr=XLLP_ACCR_S_HSS(clk->accr, XLLP_PM_ACCR_HSS_208M);
        clk->accr=XLLP_ACCR_S_SFLFS(clk->accr, XLLP_PM_ACCR_SFLFS_312M);
        clk->accr=XLLP_ACCR_S_SMCFS(clk->accr, XLLP_PM_ACCR_SMCFS_208M);
        clk->accr=XLLP_ACCR_S_VAUF(clk->accr, XLLP_PM_ACCR_VAUF_156M);

        break;
    case BSP_ARGS_RUN_FREQ_6:  //806
        param.xl = 31;
        param.xn = 2;
        param.t = 1;
        clk->accr=XLLP_ACCR_S_HSS(clk->accr, XLLP_PM_ACCR_HSS_208M);
        clk->accr=XLLP_ACCR_S_SFLFS(clk->accr, XLLP_PM_ACCR_SFLFS_312M);
        clk->accr=XLLP_ACCR_S_SMCFS(clk->accr, XLLP_PM_ACCR_SMCFS_208M);
        clk->accr=XLLP_ACCR_S_VAUF(clk->accr, XLLP_PM_ACCR_VAUF_156M);

        break;
    default:
        return;

    }
    XllpPmTriggerFreqChange(  clk,
                            (XLLP_DMEM_REGISTER_T *) OALPAtoVA(MONAHANS_BASE_REG_PA_DMEMC, FALSE),
                            &param);

    EdbgOutputDebugString("ACSR 0x%x\r\n",clk->acsr);

}
//------------------------------------------------------------------------------
//
//  Function:  OEMPlatformInit
//
//  Initialize the platform.
//  It's referenced by blcommon.c, invoked after OEMDebugInit() done.
//
BOOL OEMPlatformInit(void)
{
    UINT32 AutoBootDelay = 0;
    BOOL bXIPMode     = TRUE;
    BOOL bCFGChanged  = FALSE;
    SYSTEMTIME SysTime;
    UINT32 StartTime, CurrTime, PrevTime;
    UINT32 Selection;

    EdbgOutputDebugString("Microsoft Windows CE Ethernet Bootloader %d.%d for the Marvell Littleton Development Platform Built %s\r\n",
                          EBOOT_VERSION_MAJOR, EBOOT_VERSION_MINOR, __DATE__);

    // 
    memset((LPVOID) g_pBSPArgs, 0, sizeof(BSP_ARGS));

    LoadEBSPCFG(g_pBSPArgs);

    if (g_pBSPArgs->header.signature != OAL_ARGS_SIGNATURE ||
        g_pBSPArgs->header.oalVersion != OAL_ARGS_VERSION ||
        g_pBSPArgs->header.bspVersion != BSP_ARGS_VERSION ||
        g_pBSPArgs->argsLength != sizeof(BSP_ARGS) )
    {
        ResetDefaultBSPCFG(g_pBSPArgs);
    }

    memcpy(&g_OldBSPArgs, g_pBSPArgs, sizeof(g_OldBSPArgs));

    g_pBSPArgs->bFormatPartition = FALSE;
    g_JumpDirectlyIntoRAM        = SkipImageCopy(g_pBSPArgs);

    pic_init(g_pBSPArgs->SpecialECO); // put Voltage change here, before frequency change.
    if (g_pBSPArgs->RunFreq >= BSP_ARGS_RUN_FREQ_2)
        DoChangeFreq(g_pBSPArgs->RunFreq);

    if (g_pBSPArgs->BatteryEnable)
    {
        // Note: The battery charger is also enabled in IPL.
        // This is redundent, but the battery charger must be enabled 
        // as early as possible to insure the battery doesn't run out 
        // of juice before we boot.  For production code that includes
        // EBoot the IPL call may be omited.
        //
        // Detect a battery charging source if available
        //
        EnableBatteryCharger(
            OALPAtoVA(MONAHANS_BASE_REG_PA_OST, FALSE), 
            OALPAtoVA(MONAHANS_BASE_REG_PA_I2C, FALSE), 
            OALPAtoVA(MONAHANS_BASE_REG_PA_MFP, FALSE)
            );
    }
    else
    {
        EdbgOutputDebugString("Battery charger disabled in EBOOT config.\r\n");
    }

    DumpACCR();

    // Detect LCD panel
   // g_pEbootCFG->drvCfg[CFG_LCD_PANEL] = DetectLCDPanel();

    ConfigPrint();

    // Read the current wall-clock time.
    // NOTE: At reset, the RTC value is set to 1/1/1980 at 0:0:0.
    //
    OEMGetRealTime(&SysTime);


    // User menu code...
    //
    AutoBootDelay = g_pEbootCFG->delay;
    if (g_pEbootCFG->autoDownloadImage)
    {
        g_DownloadImage = TRUE;
        EdbgOutputDebugString ("\r\nPress [ENTER] to download now or [SPACE] to cancel.\r\n");
        EdbgOutputDebugString ("\r\nInitiating image download in %d seconds. ", AutoBootDelay--);
    }
    else
    {
        g_DownloadImage = FALSE;
        EdbgOutputDebugString ( "\r\nPress [ENTER] to launch image stored in flash or [SPACE] to cancel.\r\n");
        EdbgOutputDebugString ( "\r\nInitiating image launch in %d seconds. ", AutoBootDelay--);
    }

    // Get a snapshot of the RTC seconds count.
    //
    StartTime     = OEMEthGetSecs();
    PrevTime      = StartTime;
    CurrTime      = StartTime;
    Selection     = 0;

    // Allow the user an amount of time to halt the auto boot/download process.
    // Count down to 0 before proceeding with default operation.
    //
    while ((CurrTime - StartTime) < g_pEbootCFG->delay)
    {
        UINT8 i=0;
        UINT8 j;
        UINT8 x,y,z;

        Selection = OEMReadDebugByte();

        // Always jump to menu
        //Selection=0x20;
        if ((Selection == 0x20) || (Selection == 0x0d))
        {
            break;
        }
        CurrTime = OEMEthGetSecs();
        if (CurrTime > PrevTime)
        {
            PrevTime = CurrTime;
            if (AutoBootDelay < 9)
                i = 11;
            else if (AutoBootDelay < 99)
                i = 12;
            else if (AutoBootDelay < 999)
                i = 13;

            for (j = 0; j < i; j++)
            {
                OEMWriteDebugByte((BYTE)0x08); // print back space
            }
            x = AutoBootDelay / 100;
            y = (AutoBootDelay % 100) / 10;
            z = ((AutoBootDelay % 100) % 10);
            OEMWriteDebugLED(0, ((x << 8) | (y << 4) | (z)));
            EdbgOutputDebugString("%d seconds. ", AutoBootDelay--);
        }

    }

    switch (Selection)
    {
    case 0x00: // fall through if nothing typed
    case 0x0d: // user canceled wait
        {
            if (g_pEbootCFG->autoDownloadImage)
            {
                EdbgOutputDebugString("\r\nStarting auto download ... \r\n");
            }
            else
            {
                EdbgOutputDebugString("\r\nLaunching flash image  ... \r\n");
            }
            break;
        }
    case 0x20:
        {
            g_JumpDirectlyIntoRAM = FALSE;  // RAM corrupted.
            BLMenu();
        }
    }

    if (memcmp(&g_OldBSPArgs, g_pBSPArgs, sizeof(g_OldBSPArgs)) != 0)
    {
        StoreBSPCFG(g_pBSPArgs);
    }

    // Provide the chosen debug serial port address to the OS image for debug output messages.
    //
    // If we need to download an image, locate and initialize an Ethernet controller.
    //
    if ((g_DownloadImage) )
    {
        
        // Try each of the boot devices in the specified order (until one succeeds or they all fail).
        //
        if (InitEthernet())
        {

            // Make sure MAC address has been programmed.
            // The InitEthernet() will update the mac[] by MAC info inside the chip.
            if (!g_pBSPArgs->kitl.mac[0] && !g_pBSPArgs->kitl.mac[1] && !g_pBSPArgs->kitl.mac[2])
            {
                EdbgOutputDebugString("ERROR: Invalid Ethernet address read from Ethernet controller.\n");
                return(FALSE);
            }
            else
            {
                EdbgOutputDebugString("INFO: MAC address: %x-%x-%x-%x-%x-%x\r\n",
                     g_pBSPArgs->kitl.mac[0] & 0x00FF, g_pBSPArgs->kitl.mac[0] >> 8,
                     g_pBSPArgs->kitl.mac[1] & 0x00FF, g_pBSPArgs->kitl.mac[1] >> 8,
                     g_pBSPArgs->kitl.mac[2] & 0x00FF, g_pBSPArgs->kitl.mac[2] >> 8);
            }
        }
    }

    return(TRUE);

}

//------------------------------------------------------------------------------
//
//  Function:   OEMMultiBinNotify
//
void OEMMultiBinNotify(MultiBINInfo * pInfo)
{
    UINT32 ix;
    UINT32 dwStartAddr, dwLength;

    EdbgOutputDebugString("+OEMMultiBinNotify\r\n");

    EdbgOutputDebugString("NumRegions:%d\r\n", pInfo->dwNumRegions);

    for (ix = 0; ix < pInfo->dwNumRegions; ix++)
    {
        EdbgOutputDebugString("Start(0x%x), Length(0x%x)\r\n", pInfo->Region[ix].dwRegionStart, pInfo->Region[ix].dwRegionLength);
    }

    // Remapped the value to SDRAM in case it is FLASH
    if (pInfo->dwNumRegions == 1)
    {
        dwStartAddr = pInfo->Region[0].dwRegionStart;
        dwLength = pInfo->Region[0].dwRegionLength;

        if (!IsValidVirtualAddr(dwStartAddr))
        {
            // Check whether the image is within 64MB limit
            if ((dwStartAddr >= 0x00000000) && ((dwStartAddr+dwLength-1) <= 0x6000000))
            {                
                //pInfo->Region[0].dwRegionStart = OS_RAM_IMAGE_START;        
                EdbgOutputDebugString("****** OEMMultiBinNotify FLASH_RAM Map [0x%x => 0x%x] len=0x%x\r\n", dwStartAddr, pInfo->Region[0].dwRegionStart, dwLength);                
                g_ImageType = IMAGE_TYPE_FLASHIMAGE;
                g_EBootContext.dwBaseAddr = 0;
                g_EBootContext.dwOffsetAddr = 0;
                g_EBootContext.dwStartBlock = 0;
                g_EBootContext.dwNumBlocksWritten = 0;
            }
        }
    }

    return;
}


//------------------------------------------------------------------------------
//
//  Function:  OEMPreDownload
//
//  Pre-download initialization routine.
//
DWORD OEMPreDownload(void)
{
    UINT32 SubnetMask;
    BOOL  fGotJumpImg = FALSE, fGotIP = FALSE;
    UINT32 DHCPLeaseTime = 0;
    UINT32 *pDHCPLeaseTime = &DHCPLeaseTime;
    UINT32 BootFlags = 0;


    // Create device name based on Ethernet address (this is how Platform Builder identifies this device).
    //
    OALKitlCreateName(BSP_DEVICE_PREFIX, g_pBSPArgs->kitl.mac, g_pBSPArgs->deviceId);
    EdbgOutputDebugString("INFO: Using device name: '%s'\n", g_pBSPArgs->deviceId);

    // This is necessary to remap the correct value
    g_pOEMMultiBINNotify = OEMMultiBinNotify;

    if (g_SDMMCDownload)
        return (BL_SDMMC_DOWNLOAD);

    // If user wants to jump to existing image - skip download...
    //
    if ((!g_DownloadImage) || (g_JumpDirectlyIntoRAM))
    {
        return(BL_JUMP);
    }

    if(!(g_pBSPArgs->kitl.flags&OAL_KITL_FLAGS_DHCP))
    {
        pDHCPLeaseTime = NULL;
    }

    // Initialize the TFTP transport.
    memcpy(g_DeviceAddr.wMAC, g_pBSPArgs->kitl.mac, (sizeof(UINT16) * 3));
    g_DeviceAddr.dwIP  = g_pBSPArgs->kitl.ipAddress;
    g_DeviceAddr.wPort = 0;
    SubnetMask         = g_pBSPArgs->kitl.ipMask;

    //EdbgOutputDebugString("here\n");

    // Init memory
    //EdbgOutputDebugString("initializing RAM from 0x%X to 0x%X...", OS_RAM_IMAGE_START, OS_RAM_END);
    //memset((void*) OALPAtoVA(OS_RAM_IMAGE_START, FALSE), 0xFF, OS_RAM_END - OS_RAM_IMAGE_START);
    //EdbgOutputDebugString("done\r\n");

    if (!EbootInitEtherTransport(&g_DeviceAddr,
                                 &SubnetMask,
                                 &fGotJumpImg,
                                 pDHCPLeaseTime,
                                 EBOOT_VERSION_MAJOR,
                                 EBOOT_VERSION_MINOR,
                                 BSP_DEVICE_PREFIX,
                                 g_pBSPArgs->deviceId,
                                 EDBG_CPU_ARM720,
                                 BootFlags))
    {
        return (BL_ERROR);
    }


    return(fGotJumpImg? BL_JUMP : BL_DOWNLOAD);
}


void DumpTOC(const ROMHDR* pTOC);
//------------------------------------------------------------------------------
//
//  Function:  OEMLaunch
//
//  Launch downloaded/stored image.
//
void OEMLaunch(DWORD dwImageStart, DWORD dwImageLength, DWORD dwLaunchAddr, const ROMHDR *pRomHdr)
{
    EDBG_OS_CONFIG_DATA *pCfgData;
    EDBG_ADDR EshellHostAddr;
    UINT32 PhysAddress;
    P_XLLP_UDC_REGISTERS_T pUSBReg;

    // According to BLCommon, this is called only when download the image completed.
    // Now we have download the Flash.bin, but we need to lauch IPL instead.
    if ( g_ImageType == IMAGE_TYPE_DEBUGNK )
    {

        EdbgOutputDebugString("\r\n\r\n\r\nCalling Launch()\r\n");
        Launch(0x800B0000);
        EdbgOutputDebugString("Launch() unexpected returned!\r\n");

        // Should never get here...
        //
        SpinForever();

    }

#ifndef OSV_WINCENET
    if (((g_DownloadImage) && (!g_IPLDownload) && (!g_JumpDirectlyIntoRAM))
        || ((g_SDMMCDownload) && (!g_IPLDownload) && (!g_JumpDirectlyIntoRAM)))
    {        
        LoadImageDDR(TRUE);
        // If in here, not suppose to come back!!!
    }
    // Load IPL into IMAGE_IPL_RAM_UA_START
#endif

    // Quick hack to find pTOC in image
    do
    {
        // EBoot and the kernel's view of RAM differs. This is because the CE OEMAddressTable
        // maps RAM to 0x80000000, while in Eboot, it's that 0x84000000 (as Eboot doesn't
        // work in RAM, but the SRAM of Monohans).
        // Launch IPL instead of NK.bin
        //DWORD* ram_ptr = (DWORD*) 0xA0100000;
#ifdef OSV_WINCENET
        DWORD* ram_ptr = (DWORD*)(OS_RAM_IMAGE_START + 0x20000000);
#else
        DWORD* ram_ptr = (DWORD*) IMAGE_IPL_RAM_UA_START;
#endif
        DWORD* ptoc_area = ram_ptr + 16; // 64 bytes in is the NK signature area, I think
        DWORD  ptoc_addr;
        ROMHDR* nk_pTOC;

        EdbgOutputDebugString("Print out pTOC location from beginning (offset 0x40) from ram ptr 0x%x\r\n", ram_ptr);
        EdbgOutputDebugString("Signature: 0x%X\r\n", *(ptoc_area + 0));
        EdbgOutputDebugString("Virt Addr: 0x%X\r\n", *(ptoc_area + 1));
        EdbgOutputDebugString("Offset   : 0x%X\r\n", *(ptoc_area + 2));

        // Attempt to decode pTOC area
        ptoc_addr = *(ptoc_area + 1);
        // Adjust for RAM offset
        //ptoc_addr += 0x4000000;
        // Our pointer.
        nk_pTOC = (ROMHDR*) ptoc_addr;

        // dump pTOC
        DumpTOC(nk_pTOC);
    } while (0);


    // If a launch address wasn't specified - use the last known good address.
    //
    if (!dwLaunchAddr)
    {
        dwLaunchAddr = g_pEbootCFG->dwLaunchAddr;
    }
    else
    {
        if (g_pEbootCFG->dwLaunchAddr != dwLaunchAddr ||
            g_pEbootCFG->dwPhysStart  != dwImageStart ||
            g_pEbootCFG->dwPhysLen    != dwImageLength)
        {
            g_pEbootCFG->dwLaunchAddr = dwLaunchAddr;
            g_pEbootCFG->dwPhysStart  = dwImageStart;
            g_pEbootCFG->dwPhysLen    = dwImageLength;
            g_pEbootCFG->dwStoreAddr  = 0;

            StoreBSPCFG(g_pBSPArgs);
        }
    }

    // Translate the image start address (virtual address) to a physical address.
    //
    PhysAddress = (UINT32) OALVAtoPA((VOID *)dwLaunchAddr);
    EdbgOutputDebugString("Download successful!  Jumping to image at 0x%x (physical 0x%x)...\r\n", dwLaunchAddr, PhysAddress);

    // Wait for PB connection...
    //
    if (g_DownloadImage)
    {
        if (g_IPLDownload)
        {
            g_ImageType = IMAGE_TYPE_IPL;
            if (g_FlashInfo.wDataBytesPerSector == LB_BYTES_PER_PAGE )
                OEMWriteFlash(IMAGE_IPL_RAM_PA_START, LB_FLASH_BLOCK_IPL_LENGTH*LB_BYTES_PER_PAGE*LB_PAGES_PER_BLOCK);
            else
                OEMWriteFlash(IMAGE_IPL_RAM_PA_START, FLASH_BLOCK_IPL_LENGTH*SB_BYTES_PER_PAGE*SB_PAGES_PER_BLOCK);

        }

        if (!(pCfgData = EbootWaitForHostConnect(&g_DeviceAddr, &EshellHostAddr)))
        {
            EdbgOutputDebugString("ERROR: EbootWaitForHostConnect failed!\r\n");
            SpinForever();
        }

        // If the user selected "passive" KITL (i.e., don't connect to the target at boot time), set the
        // flag in the args structure so the OS image can honor it when it boots.
        //
    }

    EdbgOutputDebugString("Disconnect USB\r\n");
    //USB Software disconnect
    pUSBReg=(P_XLLP_UDC_REGISTERS_T)OALPAtoVA(MONAHANS_BASE_REG_PA_UDC, FALSE);
    pUSBReg->up2ocr=0;

    // Jump to the image we just downloaded.
    //
    EdbgOutputDebugString("\r\n\r\n\r\nCalling Launch()\r\n");
    EdbgOutputDebugString("                                \r\n\r\n\r\n"); // flush FIFOs.
    Launch(PhysAddress);
    EdbgOutputDebugString("Launch() unexpected returned!\r\n");

    // Should never get here...
    //
    SpinForever();

}


static BOOL InitEthernet(void)
{
    UINT32 EthDevice;
    // This table describes the boot order for a given configuration.
    //

    if (g_InitEther)
        return TRUE;


    EthDevice = InitSpecifiedEthDevice(&g_pBSPArgs->kitl, 0);


    if (EthDevice == -1)
        return FALSE;

    g_InitEther = TRUE;
    return TRUE;
}


//------------------------------------------------------------------------------
//
//  Function:  StoreBSPCFG
//
//  Stores bootloader configuration information (menu settings, etc.) in flash.
//
BOOL StoreBSPCFG(BSP_ARGS *pArgs)
{
    UINT32 cfgStart;
    if (g_FlashInfo.wDataBytesPerSector == LB_BYTES_PER_PAGE)
        cfgStart = LB_FLASH_BLOCK_EBOOTCFG_START;
    else
        cfgStart = FLASH_BLOCK_EBOOTCFG_START;
    
    if (!FlashWriteNAND(cfgStart, (PUCHAR)pArgs, sizeof(BSP_ARGS)))
    {
        EdbgOutputDebugString("ERROR: StoreBSPCFG: failed to write configuration.\r\n");
        return(FALSE);
    }

    return (TRUE);
}




//------------------------------------------------------------------------------
//
//  Function:  LoadEBSPCFG
//
//  Retrieves bootloader configuration information (menu settings, etc.) from flash.
//
BOOL LoadEBSPCFG(BSP_ARGS *args)
{
    UINT32 cfgStart;
    EdbgOutputDebugString("Loading EBOOT configuration...\r\n");

    if (g_FlashInfo.wDataBytesPerSector == LB_BYTES_PER_PAGE)
        cfgStart = LB_FLASH_BLOCK_EBOOTCFG_START;
    else
        cfgStart = FLASH_BLOCK_EBOOTCFG_START;

    if (!FlashReadNAND(cfgStart, (PUCHAR)args, sizeof(BSP_ARGS)))
    {
        EdbgOutputDebugString("ERROR: LoadEBSPCFG: failed to load configuration.\r\n");
    }
    // Is the CFG data valid?  Check for the magic number that was written the last time
    // the CFG block was updated.  If Eboot has never been run, there will be no configuration
    // information, so the magic number will likely not be found.  In this case, setup the 
    // factory defaults and store them into Flash.
    //
    if (args->eBootCFG.ConfigMagicNumber != EBOOT_CFG_MAGIC_NUMBER)
    {
        ResetDefaultBSPCFG(g_pBSPArgs);
    }

    // Make sure a valid debug serial port address (physical) was found.
    //
    if (args->dbgSerPhysAddr !=	MONAHANS_BASE_REG_PA_SIRUART)
    {
        args->dbgSerPhysAddr = MONAHANS_BASE_REG_PA_SIRUART;
    }

    EdbgOutputDebugString("LoadEBSPCFG: Magic Number: %x\r\n", 	args->eBootCFG.ConfigMagicNumber);

    return(TRUE);
}



/// Set MAC address of Ethernet adapter via Eboot menu. 
/// @param  pBspArg, pointer to BSP_ARGS structure.
void SetMACAddress(BSP_ARGS * pBspArg)
{
#define MAC_ADDR_NUM_CHAR 13
    BYTE  *p = (BYTE *) pBspArg->kitl.mac;
    char buffer[MAC_ADDR_NUM_CHAR+1]; 
    char  *pszDottedD = buffer;
    char  *pszLastNum = buffer;
    WORD cwNumChars = 0;
    UINT16 InChar = 0;
    UINT16 i = 0;

    EdbgOutputDebugString ( "\r\nEnter MAC address (xx-xx-xx-xx-xx-xx):");

    while (!((InChar == 0x0d) || (InChar == 0x0a)))
    {
        InChar = OEMReadDebugByte();
        if (InChar != OEM_DEBUG_COM_ERROR && InChar != OEM_DEBUG_READ_NODATA)
        {
            // If it's a number or a period, add it to the string
            if ((InChar == '-') || (InChar >= '0' && InChar <= '9') ||
                ((InChar >= 'A') && (InChar <= 'F')) ||
                ((InChar >= 'a') && (InChar <= 'f')))
            {
                if ((InChar >= 'a') && (InChar <= 'f'))
                {
                    InChar = InChar - 'a' + 'A'; // Capitalize.
                }
                if (cwNumChars < 18)
                {
                    buffer[cwNumChars++] = (char)InChar;
                    OEMWriteDebugByte((BYTE)InChar);
                }
            }
            // If it's a backspace, back up
            else if (InChar == 8)
            {
                if (cwNumChars > 0)
                {
                    cwNumChars--;
                    OEMWriteDebugByte((BYTE)InChar);
                }
            }
        }
    }

    // If it's a carriage return with an empty string, don't change anything.
    if (cwNumChars)
    {
        buffer[cwNumChars] = '\0';
#ifdef ETHERNET_EEPROM
        if(pBspArg->kitl->devLoc->PhysicalLoc == (LITTLETON_BASE_REG_PA_SMSC_ETHERNET + 0x300))
        {
            EthernetSetMACAddress(buffer);
        }
#endif
        for( cwNumChars = 0; cwNumChars < 6; cwNumChars++ )
        {
            while(*pszDottedD != '-' && *pszDottedD != '\0')
                pszDottedD++;
            if (*pszDottedD == '\0' && cwNumChars != 5)
            {
                // zero out the rest of MAC address
                while(i++ < 6)
                {
                    *p++ = 0;
                }
                break;
            }
            *pszDottedD = '\0';
            *p++ = (hex_atoi(pszLastNum) & 0xFF);
            i++;
            pszLastNum = ++pszDottedD;
        }

        //EdbgOutputDebugString ( "\r\nMAC address programming complete.\r\n");
    }
}

static void SetBootMe(EBOOT_CFG *pEbootCFG)
{
    char szCount[16];
    WORD cwNumChars = 0;
    UINT16 InChar = 0;

    EdbgOutputDebugString ( "\r\nUse 0 for continuous boot me packets. \r\n");
    EdbgOutputDebugString ( "Enter maximum number of boot me packets to send [0-255]: ");

    while (!((InChar == 0x0d) || (InChar == 0x0a)))
    {
        InChar = OEMReadDebugByte();
        if (InChar != OEM_DEBUG_COM_ERROR && InChar != OEM_DEBUG_READ_NODATA)
        {
            // If it's a number or a period, add it to the string
            if ((InChar >= '0' && InChar <= '9'))
            {
                if (cwNumChars < 16)
                {
                    szCount[cwNumChars++] = (char)InChar;
                    OEMWriteDebugByte((BYTE)InChar);
                }
            }
            // If it's a backspace, back up
            else if (InChar == 8)
            {
                if (cwNumChars > 0)
                {
                    cwNumChars--;
                    OEMWriteDebugByte((BYTE)InChar);
                }
            }
        }
    }

    // If it's a carriage return with an empty string, don't change anything.
    if (cwNumChars)
    {
        szCount[cwNumChars] = '\0';
        pEbootCFG->numBootMe = atoi(szCount);
        if (pEbootCFG->numBootMe > 255)
        {
            pEbootCFG->numBootMe = 255;
        }
        else if (pEbootCFG->numBootMe < 0)
        {
            pEbootCFG->numBootMe = 1;
        }
    }
}


/// Verify memory of image.
/// Will set the g_ImageType according to the image start address and length.
/// @param  dwStartAddr [IN]Image start address.
/// @param  dwLength    [IN]Image length.
/// @return     TRUE/FALSE Verify success or not
BOOL OEMVerifyMemory(DWORD dwStartAddr, DWORD dwLength)
{
    BOOL  rc;
    DWORD dwPhysVerifyStart; 
    DWORD dwPhysVerifyEnd;

    // Since verify in OEMMultiBinary, no need to process again.
    if (g_ImageType == IMAGE_TYPE_FLASHIMAGE)
    {
        EdbgOutputDebugString("*******OEMVerifyMemory : Flash Image\r\n");
        g_pBSPArgs->bFormatPartition = TRUE;
        return TRUE;
    }

    dwPhysVerifyStart = (DWORD) OALVAtoPA((void *)dwStartAddr); 
    dwPhysVerifyEnd = (DWORD) OALVAtoPA((void *)(dwStartAddr + dwLength - 1));

    EdbgOutputDebugString("****** OEMVerifyMemory checking physical range [ 0x%x ==> 0x%x ]...\r\n", dwPhysVerifyStart, dwPhysVerifyEnd);

    // Support Debug Kernel Quickly. Direct NK Partition data to RAM
    if( dwStartAddr>=IMAGE_BOOT_DEBUGNK_UA && (dwStartAddr+dwLength) <= (IMAGE_BOOT_DEBUGNK_UA+0x600000 ))
    {
        EdbgOutputDebugString("****** Debug NK Partition data address ****** \r\n\r\n");
        g_ImageType = IMAGE_TYPE_DEBUGNK;
        return TRUE;
    }


    EdbgOutputDebugString("BOOT FLASH 0x%x -> 0x%x\r\n",LITTLETON_BASE_PA_BOOT_FLASH,LITTLETON_BASE_PA_BOOT_FLASH + LITTLETON_SIZE_BOOT_FLASH);
    EdbgOutputDebugString("BOOT SDRAM 0x%x -> 0x%x\r\n",LITTLETON_BASE_PA_SDRAM,LITTLETON_BASE_PA_SDRAM + LITTLETON_SIZE_SDRAM);

    if ((dwPhysVerifyStart >= LITTLETON_BASE_PA_BOOT_FLASH) && (dwPhysVerifyEnd <= (LITTLETON_BASE_PA_BOOT_FLASH + LITTLETON_SIZE_BOOT_FLASH)))
    {
#ifdef OSV_WINCENET
        EdbgOutputDebugString("****** FLASH address ****** \r\n\r\n");
        g_ImageType = IMAGE_TYPE_FLASHIMAGE;
#else
//        EdbgOutputDebugString("****** FLASH address ****** \r\n\r\n");
//        g_ImageType = IMAGE_TYPE_FLASHIMAGE;
        EdbgOutputDebugString("****** BootLoader address ****** \r\n\r\n");
        g_ImageType = IMAGE_TYPE_BOOTLOADER;
#endif
        rc = TRUE;
    }
    else if ((dwPhysVerifyStart >= LITTLETON_BASE_PA_SDRAM) && (dwPhysVerifyEnd <= (LITTLETON_BASE_PA_SDRAM + LITTLETON_SIZE_SDRAM)))
    {
        EdbgOutputDebugString("****** RAM address ****** \r\n\r\n");

        EdbgOutputDebugString(" IPL (0x%x => 0x%x)\r\n", IMAGE_IPL_RAM_PA_START, IMAGE_IPL_RAM_PA_END);
        if (dwPhysVerifyStart == IMAGE_IPL_RAM_PA_START)
        {
            EdbgOutputDebugString(" IPL image!!\r\n");
            g_ImageType = IMAGE_TYPE_IPL;
            g_IPLDownload = TRUE;
        }
        else
        {
            EdbgOutputDebugString(" OS Image\r\n");
            // Download OS, requires erasing user store.
            g_pBSPArgs->bFormatPartition = TRUE;
            g_ImageType = IMAGE_TYPE_RAMIMAGE;
        }
        rc = TRUE;
    }
    else
    {
        EdbgOutputDebugString("****** OEMVerifyMemory FAILED - Invalid Memory Area ****** \r\n\r\n");

        rc = FALSE;
    }

    EdbgOutputDebugString("ImageType = %d\r\n", g_ImageType);

    return(rc);

}

void micco_write(XLLP_I2C_T *pI2C, XLLP_OST_T *pOST, XLLP_UINT8_T reg, XLLP_UINT8_T value)
{
    XLLP_STATUS_T ret;

    ret = XllpPmMiccoWrite(pI2C, pOST, reg, value);

    if (ret != XLLP_STATUS_SUCCESS)
        EdbgOutputDebugString("write arava reg = 0x%x, value = 0x%x, returned %d failed!!!\r\n", 
                              (unsigned int)reg, (unsigned int)value, (unsigned int)ret);
}

void pic_init(BOOL is_special_eco)
{
    XLLP_VUINT32_T *pMFP;
    P_XLLP_OST_T pOST;
    P_XLLP_I2C_T pI2C;
    XLLP_INT8_T pMfpRMDb[XLLP_MFP_RM_DB_SIZE];

    EdbgOutputDebugString("pic init!\r\n");
    
    pOST= (P_XLLP_OST_T) OALPAtoVA(MONAHANS_BASE_REG_PA_OST, FALSE);
    pI2C= (P_XLLP_I2C_T) OALPAtoVA(MONAHANS_BASE_REG_PA_I2C, FALSE);
    pMFP= (XLLP_VUINT32_T *) OALPAtoVA(MONAHANS_BASE_REG_PA_MFP, FALSE);

    if (XLLP_STATUS_SUCCESS!=XllpMfpResourceManagerInit(pMfpRMDb))
    {
        EdbgOutputDebugString("[failed to XllpMfpResourceManagerInit()!\r\n");
    }
        
    XllpI2cInit(pI2C, pMFP, pMfpRMDb, 0);

    /* shutdown watchdog */
    micco_write(pI2C, pOST, 0x0b, 78);

    /* turn on backlight */
    micco_write(pI2C, pOST, 0x3c, 0x7f);

   /*  LCD power 23mA */
    micco_write(pI2C, pOST, 0x3d, 0x35);

   /* LDO6,4,3; sets MAG_2V8=2.8V(110), V_1P8=1.8V(0),LCD_2V8=2.8V(1010)*/		
    micco_write(pI2C, pOST, 0x13, 0xca);

   /* LDO9,8,7, sets LOSS_2V8=2.8V(010),LDO8=0, ACC_3V=3V(110) */
    micco_write(pI2C, pOST, 0x14, 0x46);   

   /*  LDO11,10;sets USB_1V8=1.8V(0000) BUCK2_SEL_1V9=1.8V(0) VDD_AF_2V8=2.8V(010) */
    micco_write(pI2C, pOST, 0x15, 0x02);   

   /* LDO13,12; sets WIFI_1V8=1.8V(0000) and V_SDIO=3V(1110) */
    micco_write(pI2C, pOST, 0x16, 0x0E);   

   /* LDO15,14; sets v_CI=1.8V(0000) WIFI_3V3=3.3V(1111) */
    micco_write(pI2C, pOST, 0x17, 0x0F);   

    /* APPS_OVER2; Enable LDO13, 12, 11, 10, 9, 7 & 6 */
    micco_write(pI2C, pOST, 0x11, 0xfb);   
	

    /* TODO: Add NAND workaround here, do we need it? - Littleton*/
#if 1
	micco_write(pI2C, pOST, 0x72, 0x80);
	micco_write(pI2C, pOST, 0x75, 0x35);
	micco_write(pI2C, pOST, 0x7D, 0x68);
#endif
}


long _cdecl
InterlockedIncrement(
    long * Addend
    )
{
    return ++(*Addend);
}


long _cdecl
InterlockedDecrement(
    long * Addend
    )
{
    return --(*Addend);
}
